﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;

namespace Waterfood.FolderSizeViewer
{
    public class FolderData
    {
        private readonly Dictionary<string, FolderItem> _data = new Dictionary<string,FolderItem>(100000);
        private readonly object _lock = new object();
        public string Root { get; private set; }
        public string RootEscaped { get; private set; }

        public static FolderData Create(string root)
        {
            var inst = new FolderData();
            inst.Root = Path.GetFullPath(root);
            if (!Directory.Exists(inst.Root))
            {
                throw new InvalidOperationException("root does not exist");
            }
            var sep = new char[] { Path.VolumeSeparatorChar, Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
            inst.RootEscaped = string.Join("_", inst.Root.Split(sep, StringSplitOptions.RemoveEmptyEntries));
            return inst;
        }

        public static FolderData Load(string root)
        {
            // TODO read
            return Create(root);
        }

        public long RootSize()
        {
            lock (_lock)
            {
                return _data[Root].Size;
            }
        }

        public void GetFolderInfo(string folder, out int totalFiles, out long totalSize, out List<FolderItem> children)
        {
            List<FolderItem> list = new List<FolderItem>();
            bool success = false;
            totalFiles = 0;
            totalSize = 0;
            children = null;
            lock (_lock)
            {
                if (_data.ContainsKey(folder) && _data[folder].IsFolder)
                {
                    success = true;
                    var f = _data[folder];
                    totalFiles = f.Files;
                    totalSize = f.Size;
                    foreach (var c in f.Children)
                    {
                        var p = Path.Combine(folder, c);
                        list.Add(_data[p]);
                    }
                }
            }
            if (success)
            {
                children = list.OrderByDescending(i => i.Size).ToList();
            }
        }

        public void AddFile(string name, long size)
        {
            lock (_lock)
            {
                _data.Add(name, new FolderItem(Path.GetFileName(name), false, size));
                string parent = Path.GetDirectoryName(name);
                _data[parent].Files++;
                _data[parent].Size += size;
                _data[parent].Children.Add(Path.GetFileName(name));

                parent = Path.GetDirectoryName(parent);
                while (parent != null && _data.ContainsKey(parent))
                {
                    _data[parent].Files++;
                    _data[parent].Size += size;
                    parent = Path.GetDirectoryName(parent);
                }
            }
        }

        public void AddFolder(string name)
        {
            lock (_lock)
            {
                _data.Add(name, new FolderItem(Path.GetFileName(name), true, 0));
                if (name != Root)
                {
                    string parent = Path.GetDirectoryName(name);
                    _data[parent].Children.Add(Path.GetFileName(name));
                }
            }
        }

        public void Save()
        {
            string file = RootEscaped + ".dat";
            Console.Write("Saving data to " + file + " ... ");
            Stream writer = File.Create(file);
            BinaryFormatter serializer = new BinaryFormatter();
            serializer.Serialize(writer, _data);
            writer.Close();
            Console.WriteLine("done.");
        }
    }
}
